import sys, time
import socket, asyncore 
import Queue
import simplejson
import gobject

from util import config
from net import network

from fvutil import fvcon

FLASH_DEBUG = True

POLICY = """
  <cross-domain-policy>
    <allow-access-from domain="*" to-ports="*" />
  </cross-domain-policy>
"""

class FlashInterface(gobject.GObject):
    
    """
        Messages from the UI are received through this interface and are also sent to the UI
        
        In order to receive, add the message type that is received in L{processRecv} and 
        add a signal that would be emitted for the application to respond.
        
        In order to send a message, create a method in this class and call L{put} method to 
        put it in the queue for delivery to the user interface.
    """
    
    
    __gsignals__ = {
            # create signals that will be emitted when message received from the Flash UI

            'login': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_STRING, gobject.TYPE_STRING,)),
            'send_object' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'start_record' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'stop_record' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'start_play' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'stop_play' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'poll_start_record' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'poll_stop_record' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'poll_start_play' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'poll_stop_play' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'get_groups' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'new_group' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'show_group' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'add_user_to_group' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,gobject.TYPE_PYOBJECT,)),
            'del_user_from_group' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT, gobject.TYPE_PYOBJECT,)),
            'del_group' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'update_group_name' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT, gobject.TYPE_STRING,)),
            'push_to_talk' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'open_object' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,gobject.TYPE_INT,)),
            'edit_object' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            'edit_quality' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'get_messages' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,)),
            'get_message_count' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,)),
            'start_tivo' :  (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT, gobject.TYPE_PYOBJECT,)),
            'stop_tivo' :  (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'exclude_me_from_tivo' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
    }

    class Listener(asyncore.dispatcher):
        def __init__(self, host, port, data_cb, init_cb):
            asyncore.dispatcher.__init__(self)
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.setblocking(0)
            self.bind((host, port))
            self.channel = None
            self.__host = host
            self.__port = port
            self.__data_cb = data_cb
            self.__init_cb = init_cb
            self.listen(1)
            print 'FlashInterface.Listener bound to ',host,port
            
        def handle_accept(self):
            channel, address = self.accept()
            self.channel = FlashChannel(channel, self.__host, self.__port)
            self.channel.add_receive_listener(self.__data_cb)
            self.__init_cb()
        
    
    def __init__(self, iface, local):
        gobject.GObject.__init__(self)
        if local:
            host = 'localhost'
        else:
            host = network.get_ip_addr(iface)
        port = config.get_int('fv', 'flash-port')
        self.__commands = {}
        for (name, value) in config.items('fv.ui'):
            self.__commands[name.upper()] = int(value)
        
        types = {}
        for (name, value) in config.items('fv.types'):
            types[name.upper()] = int(value)
        
        self.__commands['types'] = types
        
        self.__listener = FlashInterface.Listener(host, port, self.__process_recv, self.__initialize)
        
    def __put(self, msg):
        if self.__listener.channel is not None:
            self.__listener.channel.toflash.put(msg)

    def __process_recv( self, data ):
        """
            Callback for responding to Flash user actions
            Pass to L{FlashChannel.addReceiveListener}
            @type data: JSON data
            @param data: dictionary of command encoded in JSON
        """
        local = False
        if FLASH_DEBUG:
            print "flash interface received: ",  data
        data_list = data.split('\x00')
        
        if data_list[0] == '<policy-file-request/>':
            # Flash asking what socket policy is
            print 'policy'
            self.__put(POLICY)
            return
        
        # Format : JSON-encoded data
        for data in data_list[:-1]:
            if len(data) == 0:
                # This is probably a disconnect
                return

            # decode
            data = simplejson.loads(data)
            
            if FLASH_DEBUG:
                print "flash interface decoded into ", data
            
            # data is a dictionary
            header = data['command']
            
            if header == fvcon.LOGIN:
                print "LOGIN"
                self.emit("login", data['username'], data['password'])
            elif header == fvcon.EDIT_OBJECT:
                print 'edit-object'
                self.emit('edit_object', data )
            elif header == fvcon.EDIT_QUALITY:
                print "EDIT_QUALITY", data['quality']
                self.emit('edit_quality', data['quality'])
            elif header == fvcon.REQUEST_START_RECORD_VOICE:
                print "Start record"
                self.emit("start_record", data )
            elif header == fvcon.REQUEST_STOP_RECORD_VOICE:
                print "Stop recording"
                self.emit("stop_record", data )
            elif header == fvcon.REQUEST_START_PLAY_VOICE:
                print "Start play voice"
                self.emit("start_play", data )
            elif header == fvcon.REQUEST_STOP_PLAY_VOICE:
                print "Stop play voice"
                self.emit("stop_play", data )
            elif header == fvcon.POLL_START_RECORD:
                print "Start record poll"
                self.emit("poll_start_record", data )
            elif header == fvcon.POLL_STOP_RECORD:
                print "Stop record poll"
                self.emit("poll_stop_record", data )
            elif header == fvcon.POLL_START_PLAY:
                print "Start play poll"
                self.emit("poll_start_play", data )
            elif header == fvcon.POLL_STOP_PLAY:
                print "Stop play poll"
                self.emit("poll_stop_play", data )
            elif header == fvcon.SEND_OBJECT:
                print "Send voice or text"
                self.emit("send_object", data )
            elif header == fvcon.OPEN_OBJECT:
                print "Get msgs"
                self.emit("open_object", data["id"], data["type"])
            elif header == fvcon.QUALITY_BAD:
                print "Quality bad"
            elif header == fvcon.GET_GROUPS:
                print "Get the groups"
                self.emit("get_groups", 1)
            elif header == fvcon.NEW_GROUP:
                print "Create new group"
                self.emit("new_group", 1 )
            elif header == fvcon.SHOW_GROUP:
                self.emit("show_group", data["id"])
            elif header == fvcon.ADD_USER_TO_GROUP:
                print "Add user to group"
                self.emit("add_user_to_group", data['id'], data['user_id'])
            elif header == fvcon.DEL_USER_FROM_GROUP:
                print "Remove user from group"
                self.emit("del_user_from_group", data['id'], data['user_id'])
            elif header == fvcon.DEL_GROUP:
                print "Delete a group"
                self.emit("del_group", data['id'])
            elif header == fvcon.UPDATE_GROUP_NAME:
                self.emit( "update_group_name", data['id'], data['label'] )
            elif header == fvcon.PUSH_TO_TALK_RELEASE:
                self.emit( "push_to_talk", 0 )
            elif header == fvcon.PUSH_TO_TALK_PRESS:
                self.emit( "push_to_talk", 1 )
            elif header == fvcon.REQUEST_ATTENTION:
                print "Attention pressed"
                self.emit( "push_to_talk", 2 )
            # methods for Timeline
            elif header == fvcon.GET_MESSAGES:
                self.emit( "get_messages", data['begin'], data['end'])
            elif header == fvcon.GET_MESSAGE_COUNT:
                self.emit( "get_message_count", data['begin'], data['end'])
            elif header == fvcon.START_TIVO:
                print "Start tivo"
                self.emit("start_tivo", data['tivo_id'], data['user_ids'])
            elif header == fvcon.STOP_TIVO:
                print "Stop tivo"
                self.emit("stop_tivo", data['tivo_id'])
            elif header == fvcon.EXCLUDE_ME_FROM_TIVO:
                print "Exclude from tivo"
                self.emit("exclude_from_tivo", data['tivo_id'])
        

    
    """
        Create methods to talk to flash
        Compose message and put in the queue
    """
    
    def loginAck(self, username, init_objs):
        if username is None:
            self.__put( {'command':fvcon.ACK_LOGIN, 'objs': {}, 'username': 0 } )
        else:
            self.__put( {'command':fvcon.ACK_LOGIN, 'objs': init_objs, 'username':username} )

    def __initialize(self):
        """
            When Flash UI connects, this method is called to transfer all
            command value pair mapping
        """
        self.__put( {'command':fvcon.INITIALIZE, 'commands': self.__commands} )
        
    def updateObject(self, obj, changed_obj):
        """
            @param obj: L{PresenceService}
            @param changed_obj: object that has changed, new message arrived, offline users or services
        """
        self.__put( {'command': fvcon.UPDATE_OBJECTS, 'objs':[changed_obj] } )

    def onAckPushToTalk(self, obj, ack):
        """
            @param ack: acknowledgement code, 0 for release ack, 1 for press ack
            @type ack: Number
            @param obj: L{MixerInterface}
        """
        if ack == 1:
            self.__put( {'command':fvcon.ACK_PUSH_TO_TALK_PRESS} )
        elif ack == 0:
            self.__put( {'command':fvcon.ACK_PUSH_TO_TALK_RELEASE} )
        
    def pollsent( self ):
        self.__put( {'command':fvcon.ACKCREATEPOLL} )
        
    
    def startedRecord( self, obj, id ):
        self.__put( {'command':fvcon.START_RECORD_VOICE, 'id':id} )
        
    def stoppedRecord( self, obj, id):
        self.__put( {'command':fvcon.STOP_RECORD_VOICE, 'id':id} )
        
    def startedPlay( self, obj, id ):
        self.__put( {'command':fvcon.START_PLAY_VOICE, 'id':id} )
        
    def stoppedPlay( self, obj, id ):
        self.__put( {'command':fvcon.STOP_PLAY_VOICE, 'id':id} )
        
    def onStartedTivo(self, obj, id):
        self.__put( {'command':fvcon.STARTED_TIVO, 'tivo_id':id })
        
    def onWarningTivo(self, obj, id):
        self.__put( {'command':fvcon.WARNING_TIVO, 'tivo_id':id })
        
    def sentObject(self, obj, guid, type):
        """
            @param obj: The specific object managers that confirm that this object was sent
            @param guid: guid of the object
            @param type: Type of the object that was sent out
        """
        self.__put( {'command':fvcon.ACK_SEND_OBJECT, 'id':guid, 'type':type } )
        
    def resultsReady(self, obj, obj_list):
        self.__put({'command':fvcon.UPDATE_OBJECTS, 'objs':obj_list})
        
    """
        Group related replies
    """
    def sendGroups( self, obj, groupdata ):
        """
            @param groupdata: Group ids and user ids of the members
            @type groupdata: list of [(group id, group name), (id, name), (id, name), (id, name)]
        """
        self.__put( {'command':fvcon.ACK_GET_GROUPS, 'groupdata':groupdata} )
        
    def showGroup( self, group_info ):
        """
            @param group_info: {'id': id of group, 'members':[(id,name), (id, name)..]}
        """
        group_info['command'] = fvcon.ACK_SHOW_GROUP
        self.__put( group_info )
        
    def groupAppeared( self, obj, groupobjs ):
        """
            @param groupdata: Group ids and user ids of the members
            @type groupdata: [(group id, group name), (id, name), (id, name), (id, name)]
        """
        #self.__put( {'command':GROUP_APPEARED, 'groupdata':groupdata } )
        self.__put( {'command':fvcon.UPDATE_OBJECTS, 'objs':[{'subcommand': uicon.ADD, 'id':227, 'name':'Poll->John', 'status':uicon.AVAILABLE, 'type':uicon.GROUP, 'priority':uicon.PRIORITY_HIGH}]} )
        
    def returnObject(self, metadata):
        """
            @param metadata: contains information about a requested object along with its guid
            @type metadata: Dictionary
        """

        metadata['command'] = fvcon.ACK_OPEN_OBJECT
        self.__put( metadata )
        
    def attentionRequest(self, obj, guid ):
        """
            @param guid: id of the node asking for attention
            @param obj: L{PresenceService}
        """
        self.__put( {'command':fvcon.ATTENTION, 'id':guid })
        
    def sendResult(self, obj, result ):
        """
            When the caller has all the result formatted
            L{Timeline} uses it to update the timeline
        """
        self.__put( result )

gobject.type_register(FlashInterface)

class FlashChannel( asyncore.dispatcher ):
    
    """
        Class to handle connection (send and receive messages) from the Flash UI
    """
    
    def __init__(self, channel, host, port ):
        asyncore.dispatcher.__init__(self, channel)
        self.toflash = Queue.Queue()
        self._HOST = host
        self._PORT = port
        
    def add_receive_listener( self, callback ):
        """
            @type callback: method
            @param callback: method that receives messages from the Flash UI
                            and generates appropriate signals
        """
        self.recvdMsg = callback

    def handle_connect( self ):
        pass

    def handle_read( self ):
        """
            Method that receives messages from the Flash UI
        """
        pkt, address = self.recvfrom( 512 )
        if FLASH_DEBUG:
            print "Flash UI Received:",len( pkt )
        if len(pkt) == 0:
            # close network
            self.close()
        self.recvdMsg( pkt )

    def handle_write( self ):
        """
            Sends command to Flash UI 
            Messages are queued up into self.toflash by put method
        """
        msg = self.toflash.get() 
        packet = simplejson.dumps( msg ) +  "\x00" # Need to end with an EOF
        if FLASH_DEBUG:
            print "Sent " + str( packet )
        self.sendto( packet, (self._HOST, self._PORT) )
            
    def writable( self ):
        return not self.toflash.empty()

        
if __name__ == "__main__":
    f = FlashInterface( "eth1" )
    asyncore.loop(0.01) 
